package bma.common.langutil.ai.common;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import bma.common.langutil.ai.AIUtil;
import bma.common.langutil.ai.stack.AIStack;

/**
 * 互斥执行的处理器
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 * @param <RESP_TYPE>
 */
public class AIMutexHub<RESP_TYPE, KEY_TYPE> {

	protected Map<KEY_TYPE, List<AIRuntime<RESP_TYPE>>> locks = new TreeMap<KEY_TYPE, List<AIRuntime<RESP_TYPE>>>();

	/**
	 * 互斥执行
	 * 
	 * @param fun
	 * @param stack
	 * @return
	 */
	public boolean execute(AIFunction<RESP_TYPE> fun, AIStack<RESP_TYPE> stack,
			KEY_TYPE key) {
		AIRuntime<RESP_TYPE> item = AIRuntime.create(fun, stack);
		item = push(key, item);
		if (item == null) {
			return false;
		}
		return _execute(key, item, true);
	}

	protected void executeOne(KEY_TYPE key, boolean apply) {
		AIRuntime<RESP_TYPE> item = next(key);
		if (item == null) {
			return;
		}
		_execute(key, item, apply);
	}

	protected boolean _execute(final KEY_TYPE key,
			final AIRuntime<RESP_TYPE> item, boolean apply) {
		AIStack<RESP_TYPE> newStack = new AIStack<RESP_TYPE>() {

			@Override
			public AIStack<?> getParent() {
				return item.getStack();
			}

			@Override
			public boolean success(RESP_TYPE result) {
				try {
					return item.getStack().success(result);
				} finally {
					executeOne(key, false);
				}
			}

			@Override
			public boolean failure(Throwable t) {
				try {
					return item.getStack().failure(t);
				} finally {
					executeOne(key, false);
				}
			}

		};
		try {
			if (apply) {
				return item.apply(newStack);
			} else {
				return item.run(newStack);
			}
		} catch (Throwable t) {
			return AIUtil.safeFailure(newStack, t);
		}
	}

	protected synchronized AIRuntime<RESP_TYPE> push(KEY_TYPE key,
			AIRuntime<RESP_TYPE> item) {
		List<AIRuntime<RESP_TYPE>> list = locks.get(key);
		if (list == null) {
			list = new LinkedList<AIRuntime<RESP_TYPE>>();
			locks.put(key, list);
		}

		list.add(item);
		if (list.size() > 1) {
			return null;
		}
		return item;
	}

	protected synchronized AIRuntime<RESP_TYPE> next(KEY_TYPE key) {
		List<AIRuntime<RESP_TYPE>> list = locks.get(key);
		if (list == null) {
			return null;
		}
		if (!list.isEmpty()) {
			list.remove(0);
		}
		if (list.isEmpty()) {
			locks.remove(key);
			return null;
		}
		return list.get(0);
	}
}
